#ifndef SEMINIX_IRQ_CHIP_H
#define SEMINIX_IRQ_CHIP_H

#include <seminix/irq.h>
#include <seminix/irqdesc.h>

struct irq_data;

int irq_set_chip(int irq, struct irq_chip *chip);
int irq_set_irq_type(int irq, unsigned int type);
int irq_set_handler_data(int irq, void *data);
int irq_set_msi_desc_off(int irq_base, unsigned int irq_offset,
    struct msi_desc *entry);
int irq_set_msi_desc(int irq, struct msi_desc *entry);
int irq_set_chip_data(int irq, void *data);

int irq_startup(struct irq_desc *desc);
void irq_shutdown(struct irq_desc *desc);

void mask_irq(struct irq_desc *desc);
void unmask_irq(struct irq_desc *desc);

void irq_percpu_enable(struct irq_desc *desc, int cpu);
void irq_percpu_disable(struct irq_desc *desc, int cpu);

void handle_level_irq(struct irq_desc *desc);
void handle_edge_irq(struct irq_desc *desc);
void handle_fasteoi_irq(struct irq_desc *desc);
void handle_edge_eoi_irq(struct irq_desc *desc);
void handle_percpu_irq(struct irq_desc *desc);
void handle_percpu_devid_irq(struct irq_desc *desc);

void __irq_set_handler(int irq, irq_flow_handler_t handle, int is_chained,
    const char *name);

static inline void
irq_set_handler(int irq, irq_flow_handler_t handle)
{
    __irq_set_handler(irq, handle, 0, NULL);
}

/*
 * Set a highlevel chained flow handler for a given IRQ.
 * (a chained handler is automatically enabled and set to
 *  IRQ_NOREQUEST, IRQ_NOPROBE, and IRQ_NOTHREAD)
 */
static inline void
irq_set_chained_handler(int irq, irq_flow_handler_t handle)
{
    __irq_set_handler(irq, handle, 1, NULL);
}

void irq_set_chained_handler_and_data(int irq, irq_flow_handler_t handle,
    void *data);

void irq_set_chip_and_handler_name(int irq, struct irq_chip *chip,
    irq_flow_handler_t handle, const char *name);

static inline void irq_set_chip_and_handler(int irq, struct irq_chip *chip,
                        irq_flow_handler_t handle)
{
    irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}

void handle_fasteoi_ack_irq(struct irq_desc *desc);
void handle_fasteoi_mask_irq(struct irq_desc *desc);

void irq_chip_enable_parent(struct irq_data *data);
void irq_chip_disable_parent(struct irq_data *data);
void irq_chip_ack_parent(struct irq_data *data);
void irq_chip_mask_parent(struct irq_data *data);
void irq_chip_unmask_parent(struct irq_data *data);
void irq_chip_eoi_parent(struct irq_data *data);
int irq_chip_set_affinity_parent(struct irq_data *data,
                 const struct cpumask *dest, bool force);
int irq_chip_set_type_parent(struct irq_data *data, unsigned int type);
int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);

/* Inline functions for support of irq chips on slow busses */
static inline void chip_bus_lock(struct irq_desc *desc)
{
    if (unlikely(desc->irq_data.chip->irq_bus_lock))
        desc->irq_data.chip->irq_bus_lock(&desc->irq_data);
}

static inline void chip_bus_sync_unlock(struct irq_desc *desc)
{
    if (unlikely(desc->irq_data.chip->irq_bus_sync_unlock))
        desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data);
}

static inline struct irq_chip *irq_data_get_irq_chip(struct irq_data *d)
{
    return d->chip;
}

static inline void *irq_data_get_irq_chip_data(struct irq_data *d)
{
    return d->chip_data;
}

static inline void *irq_data_get_irq_handler_data(struct irq_data *d)
{
    return d->handler_data;
}

/*
 * Entry/exit functions for chained handlers where the primary IRQ chip
 * may implement either fasteoi or level-trigger flow control.
 */
static inline void chained_irq_enter(struct irq_chip *chip,
                     struct irq_desc *desc)
{
    /* FastEOI controllers require no action on entry. */
    if (chip->irq_eoi)
        return;

    if (chip->irq_mask_ack) {
        chip->irq_mask_ack(&desc->irq_data);
    } else {
        chip->irq_mask(&desc->irq_data);
        if (chip->irq_ack)
            chip->irq_ack(&desc->irq_data);
    }
}

static inline void chained_irq_exit(struct irq_chip *chip,
                    struct irq_desc *desc)
{
    if (chip->irq_eoi)
        chip->irq_eoi(&desc->irq_data);
    else
        chip->irq_unmask(&desc->irq_data);
}

#endif /* !SEMINIX_IRQ_CHIP_H */
